/*
	klib_keyboard_buffer.c

	contains functionality for the keyboard buffer kernel service

	Author: Aidan Goddard 6/10/13
*/

#include"headers/ktypes.h"
#include"headers/klib_keyboard_buffer.h"

#include"../kernel/headers/scheduler_functions.h"
#include"../kernel/headers/bakery_lock.h"
#include"../kernel/headers/kernel_tables.h"

// data structures
volatile static uint32_t scan_count = 0;
volatile static uint32_t driver_count = 0;
volatile static uint64_t main_buffer[256];
void (*func_num_led[128])(int state);
void (*func_cap_led[128])(int state);
void (*func_scr_led[128])(int state);
void (*func_reset[128])(void *params);

// locks
static SCH_CLS buffer_lock;
static SCH_CLS driver_lock;


uint32_t __KLIB__KBBufferWrite(int count, uint64_t *in_buffer)
{
	// get lock on main buffer
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&buffer_lock, CPU);

	// check if buffer is full
	if(scan_count > 255)
	{
		// release lock
		BakeryLeave(&buffer_lock, CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");

		return KB_BUFFER_FULL;
	}

	// copy count scan codes into the buffer
	int i;
	for(i = 0; i < count; i++)
	{
		// copy the code in
		main_buffer[scan_count++] = in_buffer[i];

		// check if buffer is full
		if(scan_count > 255)
		{
			// release lock
			BakeryLeave(&buffer_lock, CPU);
			__asm__ __volatile__("":::"memory");
			__asm__ __volatile__("sti");
			__asm__ __volatile__("":::"memory");

			return KB_BUFFER_FULL;
		}
	}

	// release lock
	BakeryLeave(&buffer_lock, CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// finished
	return KB_SUCCESS;
}

uint32_t __KLIB__KBBufferRead(int *count, uint64_t *out_buffer)
{
	// get lock on main buffer
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&buffer_lock, CPU);

	// copy and clear
	int i;
	for(i = 0; i < scan_count; i++)
	{
		out_buffer[i] = main_buffer[i];
	}
	*count = scan_count;
	scan_count = 0;

	// release lock
	BakeryLeave(&buffer_lock, CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// return success
	return KB_SUCCESS;
}

uint32_t __KLIB__KBRegisterDriver(void(*num_led)(int state), void(*cap_led)(int state), void(*scr_led)(int state), void(*reset)(void *params), uint32_t *ID)
{
	// get lock on main buffer
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&driver_lock, CPU);

	// find a free slot
	int new_id = -1;
	int i;
	for(i = 0; i < 256; i++)
	{
		if(func_reset[i] == NULL)
		{
			// fill in the function pointers for this ID
			func_reset[i] = reset;
			func_num_led[i] = num_led;
			func_scr_led[i] = scr_led;
			func_cap_led[i] = cap_led;

			// set the highest ID if necessary
			if(i >= driver_count)
			{
				driver_count = i + 1;
			}

			// done
			new_id = i;
			break;
		}
	}

	// release lock
	BakeryLeave(&driver_lock, CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// check that it was successfull
	if(new_id == -1)
	{
		return KB_NO_FREE_DRIVER;
	}

	// return success
	return KB_SUCCESS;
}

uint32_t __KLIB__KBUnregisterDriver(uint32_t ID)
{
	// get lock on main buffer
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&driver_lock, CPU);

	// mark function entries as null for the driver
	func_reset[ID] = NULL;
	func_num_led[ID] = NULL;
	func_scr_led[ID] = NULL;
	func_cap_led[ID] = NULL;

	// if this is the highest ID, reduce it to the next highest value
	if(ID == (driver_count - 1))
	{
		int i;
		for(i = driver_count - 1; i >= 0; i--)
		{
			if(func_reset[i] != NULL)
			{
				driver_count = i + 1;
				break;
			}
		}
	}

	// release lock
	BakeryLeave(&driver_lock, CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// return success
	return KB_SUCCESS;
}

uint32_t __KLIB__KBReset(void *params)
{
	// get lock on main buffer
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&driver_lock, CPU);

	int i;
	for(i = 0; i < driver_count; i++)
	{
		if(func_reset[i] != NULL)
		{
			(*func_reset[i])(params);
		}
	}

	// release lock
	BakeryLeave(&driver_lock, CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// return success
	return KB_SUCCESS;
}

uint32_t __KLIB__KBSetNumLED(int state)
{
	// check the state variable
	if((state != KB_LED_STATE_OFF) && (state != KB_LED_STATE_ON))
	{
		return KB_BAD_STATE;
	}

	// get lock on main buffer
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&driver_lock, CPU);

	int i;
	for(i = 0; i < driver_count; i++)
	{
		if(func_num_led[i] != NULL)
		{
			(*func_num_led[i])(state);
		}
	}

	// release lock
	BakeryLeave(&driver_lock, CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// return success
	return KB_SUCCESS;
}

uint32_t __KLIB__KBSetCapLED(int state)
{
	// check the state variable
	if((state != KB_LED_STATE_OFF) && (state != KB_LED_STATE_ON))
	{
		return KB_BAD_STATE;
	}

	// get lock on main buffer
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&driver_lock, CPU);

	int i;
	for(i = 0; i < driver_count; i++)
	{
		if(func_cap_led[i] != NULL)
		{
			(*func_cap_led[i])(state);
		}
	}

	// release lock
	BakeryLeave(&driver_lock, CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// return success
	return KB_SUCCESS;
}

uint32_t __KLIB__KBSetScrLED(int state)
{
	// check the state variable
	if((state != KB_LED_STATE_OFF) && (state != KB_LED_STATE_ON))
	{
		return KB_BAD_STATE;
	}

	// get lock on main buffer
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();
	BakeryEnter(&driver_lock, CPU);

	int i;
	for(i = 0; i < driver_count; i++)
	{
		if(func_scr_led[i] != NULL)
		{
			(*func_scr_led[i])(state);
		}
	}

	// release lock
	BakeryLeave(&driver_lock, CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// return success
	return KB_SUCCESS;
}
